package com.urbainski.componentes.form
{
	import com.urbainski.componentes.form.IDataFormItem;
	import com.urbainski.componentes.form.MyDataFormItem;
	import com.urbainski.componentes.form.CSMessage;
	
	import flash.events.Event;
	import flash.events.IEventDispatcher;
	
	import mx.core.IDataRenderer;
	import mx.core.IPropertyChangeNotifier;
	import mx.core.IVisualElementContainer;
	import mx.core.UIComponent;
	import mx.core.mx_internal;
	import mx.events.FlexEvent;
	import mx.events.ValidationResultEvent;
	import mx.utils.ObjectProxy;
	import mx.validators.Validator;
	
	import spark.components.Form;
	import spark.events.ElementExistenceEvent;
	
	use namespace mx_internal;
	
	/**
	 *  Dispatched when the <code>resource</code> property changed.
	 *
	 *  @eventType "resourceChange"
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion Flex 4.5.1
	 */
	[Event(name="resourceChange", type="flash.events.Event")]
	
	/**
	 *  Dispatched when the <code>data</code> property changes.
	 * 
	 *  @eventType mx.events.FlexEvent.DATA_CHANGE
	 * 
	 */
	[Event(name="dataChange", type="mx.events.FlexEvent")]
	
	/**
	 *  Dispatched when the <code>isValid</code> property changes.
	 * 
	 *  @eventType "isValidChange"
	 * 
	 */
	[Event(name="isValidChange", type="flash.events.Event")]
	
	/**
	 *  This component assist the developers in data management process. It works in pair
	 *  with DataFormItem components. The DataForm allows you to specify the data source object
	 *  on the top level of your form and don't push it to every DataFormItem manually,
	 *  because it will be done automatically by the form.  
	 * 
	 *  <p>As every Clear component the DataForm supports customization by resources.
	 *  It allows developers easily configure the control with set of properties
	 *  or styles and doesn't require the creation of descendant for that.</p>
	 *
	 *  <p>This control dispatches a <code>flash.events.Event("resourceChange")</code> event
	 *  when <code>resource</code> property changes.</p>
	 * 
	 *   @example
	 *  The following code illustrates use of DataForm component.
	 * 
	 *  Fill the <code>data</code> property by any source Object,
	 *  specify the readable and updatable properties on event DataFormItem component
	 *  and try to update the data.
	 * 
	 *  In the example we use XML as data source to show that DataFormItem
	 *  can work with both types of data.
	 * 
	 *  <listing version="3.0">
	 *	 &lt;s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
	 *						xmlns:s="library://ns.adobe.com/flex/spark" 
	 *						xmlns:mx="library://ns.adobe.com/flex/mx"
	 *						xmlns:c="library://ns.cleartoolkit.com/flex/clear"
	 *						minWidth="955" minHeight="600"&gt;
	 *
	 *		 &lt;s:layout&gt;
	 *			&lt;s:VerticalLayout/&gt;
	 *		&lt;/s:layout&gt;
	 *
	 *		 &lt;fx:Declarations&gt;
	 *			&lt;fx:XML id="employee"&gt;
	 *				&lt;employee&gt;
	 *					&lt;name&gt;John Smith&lt;/name&gt;
	 *					&lt;email&gt;j.smith&#64;email.com&lt;/email&gt;
	 *				&lt;/employee&gt;	
	 *			&lt;/fx:XML&gt;
	 *		&lt;/fx:Declarations&gt;
	 *
	 *		&lt;c:DataForm id="dataForm"
	 *					data="{employee}"&gt;
	 *			
	 *			&lt;c:DataFormItem id="nameFI"
	 *							dataField="name"&gt;
	 *				&lt;s:TextInput id="nameTI"/&gt;
	 *			&lt;/c:DataFormItem&gt;
	 *			
	 *			&lt;c:DataFormItem id="emailFI"
	 *							dataField="email"&gt;
	 *				&lt;s:TextInput id="emailTI"/&gt;
	 *			&lt;/c:DataFormItem&gt;
	 *			
	 *		&lt;/c:DataForm&gt;
	 *
	 *
	 *		 &lt;s:Label id="employeeLabel"
	 *		 text="{'Name: ' + employee.name + '  Email: ' + employee.email}"/&gt;
	 *
	 *	&lt;/s:Application&gt;
	 *  </listing>
	 * 
	 *  @mxml <p>The <code>&lt;c:DataForm&gt;</code> tag inherits all of the tag 
	 *  attributes of its superclass and adds the following tag attributes:</p>
	 *
	 *  <pre>
	 *  &lt;c:DataForm
	 *    <strong>Properties</strong>
	 *    stretchControl=false
	 *    controlsHelperClass=clear.components.supportClasses.ControlsHelper
	 *    controlsHelper=null
	 *    data=null
	 *    dataField=null
	 *    eventType=null
	 *    properties=null
	 *    listeners=null
	 *    readOnly=false
	 *    resource=null
	 *    targetProperty=null
	 *    valueFunction=null
	 *    value=null
	 *    valueType="string"
	 *    validators=null
	 *  
	 *    <strong>Events</strong>
	 *    dataChange="<i>No default</i>"
	 *    valueChange="<i>No default</i>"
	 *    resourceChange="<i>No default</i>"
	 *  /&gt;
	 *  </pre>
	 *  
	 *  @see spark.components.Form
	 *  @see clear.resources.ResourceBase
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion Flex 4.5.1
	 */
	public class MyForm extends Form implements IDataRenderer {
		
		/**
		 * @author Cristian Elder Urbainski
		 * @since 20/12/2011
		 */
		
		public function MyForm()
		{
			super();
			
			addEventListener(ElementExistenceEvent.ELEMENT_ADD, this_elementAddHandler, true);
			addEventListener(ElementExistenceEvent.ELEMENT_REMOVE, this_elementRemoveHandler, true);
		}
		
		//----------------------------------------------------------------------
		//
		//  Variables
		//
		//----------------------------------------------------------------------
		
		//----------------------------------
		//  proxyData
		//----------------------------------
		
		/**
		 *  @private
		 *  Storage for the proxyData property.
		 */
		private var proxyData:IPropertyChangeNotifier;
		
		//----------------------------------
		//  registeredItems
		//----------------------------------
		
		/**
		 *  @private
		 * 
		 *  Array of assembled DataFormItems.
		 */
		
		private var registeredItems:Vector.<IDataFormItem> = new Vector.<IDataFormItem>();
		
		//----------------------------------
		//  registeredValidators
		//----------------------------------
		
		/**
		 *  @private
		 * 
		 *  Array of validators which were assambled from FormItems
		 *  and registered by this DataForm.
		 */
		protected var registeredValidators:Array = [];
		
		//----------------------------------
		//  invalidValidators
		//----------------------------------
		
		/**
		 *  @private
		 * 
		 *  Array of invalid validators.
		 */
		private var invalidValidators:Array = [];
		
		//----------------------------------
		//  framesToWait
		//----------------------------------
		
		/**
		 *  @private
		 * 
		 *  Number of frames that we need to wait before call <code>resetValidation()</code>.
		 */
		private var framesToWait:int = 2;
		
		
		//----------------------------------------------------------------------
		//
		//  Properties
		//
		//----------------------------------------------------------------------
		
		//----------------------------------
		//  data
		//----------------------------------
		
		/**
		 *  @private
		 *  Storage for the data property.
		 */
		private var _data:Object;		
		private var dataChanged:Boolean;
		
		[Bindable("dataChange")]
		[Inspectable(category="Data")]
		
		/**
		 *  The implementation of the <code>data</code> property
		 *  as defined by the IDataRenderer interface.
		 *  
		 *  <p>This property is Bindable; it dispatches 
		 * "dataChange" events</p>
		 *
		 *  @default null
		 *  @eventType dataChange 
		 *  @see mx.core.IDataRenderer
		 *  
		 */
		public function get data():Object
		{
			return _data;
		}
		
		/**
		 *  @private
		 */
		public function set data(value:Object):void
		{
			if (_data == value) return;
			
			_data = value;
			
			dataChanged = true;
			invalidateProperties();
			
			if (hasEventListener(FlexEvent.DATA_CHANGE))
				dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE));
		}
		
		//----------------------------------------------------------------------
		//
		//  Validation Properties
		//
		//----------------------------------------------------------------------
		
		//----------------------------------
		//  isValid
		//----------------------------------
		
		[Bindable("errorStringChanged")]
		[Inspectable(category="Validation")]
		
		/**
		 *  Specifies if controls in this DataForm were validated succesfully or not. 
		 */
		public function get isValid():Boolean
		{
			return !errorString;
		}
		
		//----------------------------------
		//  validators
		//----------------------------------
		
		/**
		 *  @private
		 *  Storage for the validators property.
		 */
		private var _validators:Array;
		private var validatorsChanged:Boolean;
		
		[Bindable("validatorsChange")]
		[InstanceType("mx.validators.Validator")]
		[Inspectable(category="Validation")]
		
		/**
		 * 	Array of validators which are related to this DataForm.
		 * 
		 *  <p>This property is Bindable; it dispatches 
		 *  "validatorsChange" events</p>
		 * 
		 *  @default null
		 *  @eventType validatorsChange 
		 * 
		 */
		public function get validators():Array
		{
			return _validators;
		}
		
		/**
		 *  @public
		 */
		public function set validators(value:Array):void
		{
			if (_validators == value) return;
			
			unregisterValidators(_validators);
			_validators = value;
			registerValidators(_validators);
			
			validatorsChanged = true;
			invalidateProperties();
			
			if (hasEventListener("validatorsChange"))
				dispatchEvent(new Event("validatorsChange"));
		}
		
		//----------------------------------
		//  trigger
		//----------------------------------
		
		/**
		 *  @private
		 *  Storage for the trigger property.
		 */
		private var _trigger:IEventDispatcher;
		
		[Bindable("triggerChange")]
		[Inspectable(category="Validation")]
		
		/**
		 *  Specifies the component generating the event that triggers the validation.
		 *  When the <code>trigger</code> dispatches a <code>triggerEvent</code>,
		 *  validation executes.
		 * 
		 *  <p>For example, if you have a "Send" button on your form,
		 *  you can specify it as a trigger and the MouseEvent.CLICK event
		 *  as a trigger event. Everytime when a user clicks on the button,
		 *  the DataForm will triggers the validation process.</p>
		 * 
		 *  <p>This property is Bindable; it dispatches 
		 *  "triggerChange" events</p>
		 * 
		 *  @default null
		 *  @eventType triggerChange
		 */
		public function get trigger():IEventDispatcher
		{
			return _trigger;
		}
		
		/**
		 *  @private
		 */
		public function set trigger(value:IEventDispatcher):void
		{
			if (_trigger == value) return;
			
			removeTriggerHandler();
			_trigger = value;
			addTriggerHandler();
			
			if (hasEventListener("triggerChange"))
				dispatchEvent(new Event("triggerChange"));
		}
		
		//----------------------------------
		//  triggerEvent
		//----------------------------------
		
		/**
		 *  @private
		 *  Storage for the triggerEvent property.
		 */
		private var _triggerEvent:String = "";
		
		[Bindable("triggerEventChange")]
		[Inspectable(category="Validation")]
		
		/**
		 *  Specifies the event that triggers the validation.
		 * 
		 *  <p>This property is Bindable; it dispatches 
		 *  "triggerEventChange" events</p>
		 * 
		 *  @default ""
		 *  @eventType triggerEventChange
		 */
		public function get triggerEvent():String
		{
			return _triggerEvent;
		}
		
		/**
		 *  @private
		 */
		public function set triggerEvent(value:String):void
		{
			if (_triggerEvent == value) return;
			
			removeTriggerHandler();
			_triggerEvent = value;
			addTriggerHandler();
			
			if (hasEventListener("triggerEventChange"))
				dispatchEvent(new Event("triggerEventChange"));
		}
		
		//----------------------------------
		//  autoResetValidation
		//----------------------------------
		
		/**
		 *  @private
		 *  Storage for the autoResetValidation property.
		 */
		private var _autoResetValidation:Boolean = true;
		
		[Bindable("autoResetValidationChange")]
		[Inspectable(category="Validation")]
		
		/**
		 *  Specifies whether the data form reset the registered validators
		 *  after the data distribution to IDataRenderer children.
		 * 
		 *  <p>If <code>true</code>, the data form calls
		 *  <code>resetValidation()</code> method right after two
		 *  <code>Event.ENTER_FRAME</code> events.
		 *  The reason for this delay is that the first frame is expended
		 *  for analysis the data inside the DataFormItem component and
		 *  the push of the value right into a control.
		 *  The second frame is expended for the value apply
		 *  to the control parts and the dispatch
		 *  of the <code>FlexEvent.VALUE_COMMIT</code> event
		 *  which will trigger the validation by default.</p>
		 *
		 *  <p>If <code>false</code>, the data form
		 *  does not reset validation results.
		 *  Your custom code is responsible for clearing
		 *  the invalid controls on this form.</p>
		 * 
		 *  <p>This property is Bindable; it dispatches 
		 *  "autoResetValidationChange" events</p>
		 * 
		 *  @default true
		 *  @eventType autoResetValidationChange
		 */
		public function get autoResetValidation():Boolean
		{
			return _autoResetValidation;
		}
		
		/**
		 *  @private
		 */
		public function set autoResetValidation(value:Boolean):void
		{
			if (_autoResetValidation == value) return;
			
			_autoResetValidation = value;
			
			if (hasEventListener("autoResetValidationChange"))
				dispatchEvent(new Event("autoResetValidationChange"));
		}
		
		
		//----------------------------------------------------------------------
		//
		//  Methods
		//
		//----------------------------------------------------------------------
		
		/**
		 *  Call this method if you ned to trigger the form validation process.
		 * 
		 *  @return Array of ValidationResultEvent instances.
		 */    
		public function validateForm():Array
		{			
			if (!registeredValidators)
			{
				errorString = "";
				
				return [];
			}
			
			/*		// We have to remove the internal handler from validators
			// that eliminate a double reaction.
			removeValidationHandler(registeredValidators);
			
			if (invalidValidators.length > 0)
			{
			invalidValidators.splice(0, invalidValidators.length);	
			}
			
			// We have to reset validation errors for ToolTipManager.
			errorString = "";
			*/		
			var validationResult:Array = Validator.validateAll(registeredValidators);
			
			if (validationResult.length > 0)
			{
				var errors:String = "";
				var num:int = validationResult.length;
				var resultEvent:ValidationResultEvent;
				for (var i:int = 0; i < num; i++)
				{
					resultEvent = validationResult[i] as ValidationResultEvent;
					var validator:Validator = resultEvent.target as Validator;
					invalidValidators.push(resultEvent.target as Validator);
					
					// Concatenate error message.
					errors += resultEvent.message + "\n";
				}
				errorString = errors;
			}
			else
			{
				errorString = "";
			}
			
			// We can add the validation handler now.
			//addValidationHandler(registeredValidators);
			
			return validationResult;
		}
		
		/**
		 *  Call this method if you need reset all validation errors on the form.
		 */    
		public function resetValidation():void
		{
			// This functinality is UNDER the Question: Should we reset invalid components or Validators.
			
			if (registeredValidators)
			{
				for each (var validator:Validator in registeredValidators)
				if (validator)
					validator.dispatchEvent(new ValidationResultEvent(ValidationResultEvent.VALID));
			}
			else if (invalidElements)
			{
				for each (var invElt:UIComponent in invalidElements)
				{
					if (invElt)
						invElt.errorString = "";
				}
			}
			
			/*if (invalidElements)
			{
			for each (var invElt:UIComponent in invalidElements)
			{
			if (invElt)
			}
			}
			return;*/
			
			/*	if (!registeredValidators)
			{
			setIsValid(true);
			errorString = "";
			return;
			}
			
			// We have to remove the internal handler from validators
			// that eliminate a double reaction.
			removeValidationHandler(registeredValidators);
			
			for each (var validator:Validator in registeredValidators)
			{
			if (validator.hasEventListener(ValidationResultEvent.VALID))
			validator.dispatchEvent(new ValidationResultEvent(ValidationResultEvent.VALID));
			}
			
			if (invalidValidators.length > 0)
			invalidValidators.splice(0, invalidValidators.length);
			
			setIsValid(true);
			errorString = "";
			
			// We can add the validation handler now.
			addValidationHandler(registeredValidators);*/
		}
		
		/**
		 *  Use this method to register validation in this form.
		 *  All registered validation will be triggered if you call <code>validateForm</code>
		 *  method.
		 * 
		 *  @param value Array of validators that the form should remember and process ad trigger in teh future.
		 */    
		public function registerValidators(value:Array):void
		{
			if (!value) return;
			
			for (var i:int = 0; i < value.length; i++ )
			{
				var currentValidator:Validator = value[i] as Validator;				
				var index:int = registeredValidators.indexOf(currentValidator);
				if (index < 0)
				{
					registeredValidators.push(currentValidator);
					
					// Temporarily fix that prevent triggering validation on data push.
					//		currentValidator.triggerEvent = "";
					
					// Add the validation handler.
					//			currentValidator.addEventListener(ValidationResultEvent.VALID,
					//											  validator_validationResultHandler);
					//			currentValidator.addEventListener(ValidationResultEvent.INVALID,
					//											  validator_validationResultHandler);
					
					if (currentValidator is IDataRenderer)
						IDataRenderer(currentValidator).data = proxyData;
				}		
			}
		}
		
		/**
		 *  Call this method if you need to remove some registered validators from the list.
		 * 
		 *  @param value Array of registered validation that should be unregistered and removed from the list.
		 */    
		public function unregisterValidators(value:Array):void
		{	
			if (!value) return;
			
			for (var i:int = 0; i < value.length; i++ )
			{
				var currentValidator:Validator = value[i] as Validator;
				var index:int = registeredValidators.indexOf(currentValidator);
				
				if (index > -1)
				{
					// Dispatch VALID event to update Form validation state.
					currentValidator.dispatchEvent(new ValidationResultEvent(ValidationResultEvent.VALID));
					
					registeredValidators.splice(index, 1);
					
					// Remove the validation handler.
					/*	if (currentValidator.hasEventListener(ValidationResultEvent.VALID))
					currentValidator.removeEventListener(ValidationResultEvent.VALID,
					validator_validationResultHandler);
					if (currentValidator.hasEventListener(ValidationResultEvent.INVALID))
					currentValidator.removeEventListener(ValidationResultEvent.INVALID,
					validator_validationResultHandler);*/
					
					if (currentValidator is IDataRenderer)
						IDataRenderer(currentValidator).data = null;
				}
				
				/*currentValidator
				if (invalidValidators != null && invalidValidators.length > 0)
				index = invalidValidators.indexOf(currentValidator);
				if (index > -1)
				{
				invalidValidators.splice(index, 1);
				// Check the form validation state.
				if (invalidValidators.length > 0)
				{
				setIsValid(false);	
				}
				else
				{
				setIsValid(true);
				errorString = "";
				}
				}*/
			}
		}
		
		/**
		 *  @private
		 *  Called when data or dataCollection changes and it requires a distribution
		 *  of a new value to children and validators which implement IDataRenderer intarface.
		 *  
		 */
		protected function distributeData(dest:Vector.<IDataFormItem> = null):void
		{			
			/*for each (var dfi:IDataFormItem in (dest ? dest : registeredItems))
			dfi.data = data;
			
			for each (var validator:Validator in registeredValidators)
			if (validator is IDataRenderer)
				IDataRenderer(validator).data = proxyData;
			*/
			if (autoResetValidation)
			{
				// Add event listener to reset validation...
				addEventListener(Event.ENTER_FRAME, this_enterFrameHandler);
				framesToWait = 2;
			}
		}
		
		////////// temporary method -----//
		
		/**
		 *  @private
		 */
		private function setData(value:Object):void
		{
			if (_data == value)	return;
			
			_data = value;
			if (hasEventListener(FlexEvent.DATA_CHANGE))
				dispatchEvent(new FlexEvent(FlexEvent.DATA_CHANGE)); 
		}
		
		/**
		 *  @private
		 */
		private function addTriggerHandler():void
		{
			if (_trigger)
				_trigger.addEventListener(_triggerEvent, trigger_triggerEventHandler);
		}
		
		/**
		 *  @private
		 */
		private function removeTriggerHandler():void
		{
			if (_trigger && _trigger.hasEventListener(_triggerEvent))  
				_trigger.removeEventListener(_triggerEvent, trigger_triggerEventHandler);
		}
		
		/**
		 *  @private
		 */
		private function addValidationHandler(validators:Array):void
		{
			if (validators)
				for each (var validator:Validator in registeredValidators)
			{
				validator.addEventListener(ValidationResultEvent.VALID,
					validator_validationResultHandler);
				validator.addEventListener(ValidationResultEvent.INVALID,
					validator_validationResultHandler);
			}
		}
		
		/**
		 *  @private
		 */
		private function removeValidationHandler(validators:Array):void
		{
			if (validators)
				for each (var validator:Validator in registeredValidators)
			{
				if (validator.hasEventListener(ValidationResultEvent.VALID))
					validator.removeEventListener(ValidationResultEvent.VALID,
						validator_validationResultHandler);
				if (validator.hasEventListener(ValidationResultEvent.INVALID))
					validator.removeEventListener(ValidationResultEvent.INVALID,
						validator_validationResultHandler);
			}
		}
		
		private function enumerateChildren(parent:Object):Vector.<IDataFormItem>
		{
			var result:Vector.<IDataFormItem> = new Vector.<IDataFormItem>();
			if (parent is IDataFormItem)
			{
				registeredItems.push(parent);
				result.push(parent);
				
				registerElement(IDataFormItem(parent));
			}
			if (parent is IVisualElementContainer && !(parent is MyForm))
				for (var i:int = 0; i < IVisualElementContainer(parent).numElements; i++)
					enumerateChildren(IVisualElementContainer(parent).getElementAt(i)).forEach(function(item:IDataFormItem, index:int, array:Vector.<IDataFormItem>):void {result.push(item)});
			
			return result;
		}
		
		private function denumerateChildren(parent:Object):Vector.<IDataFormItem>
		{
			var result : Vector.<IDataFormItem> = new Vector.<IDataFormItem>();
			if (parent is IDataFormItem)
			{
				registeredItems.splice(registeredItems.indexOf(parent), 1);
				result.push(parent);
				
				unregisterElement(IDataFormItem(parent));
			}
			if (parent is IVisualElementContainer && !(parent is MyForm))
				for (var i:int = 0; i < IVisualElementContainer(parent).numElements; i++)
					denumerateChildren(IVisualElementContainer(parent).getElementAt(i)).forEach(function(item:IDataFormItem, index:int, array:Vector.<IDataFormItem>):void {result.push(item)});
			
			return result;
		}
		
		private function registerElement(element:Object):void
		{
			// Add event listeners.
			element.addEventListener("enabledChanged", dataFormItem_enabledChangeHandler);
			element.addEventListener(FlexEvent.SHOW, dataFormItem_showHandler);
			element.addEventListener(FlexEvent.HIDE, dataFormItem_hideHandler);
			
			if (IDataFormItem(element).validators != null && IDataFormItem(element).validators.length > 0)
				registerValidators(IDataFormItem(element).validators);
		}
		
		private function unregisterElement(element:Object):void
		{
			// Remove event listeners.
			if (element.hasEventListener("enabledChanged"))
				element.removeEventListener("enabledChanged", dataFormItem_enabledChangeHandler);
			if (element.hasEventListener(FlexEvent.SHOW))
				element.removeEventListener(FlexEvent.SHOW, dataFormItem_showHandler);
			if (element.hasEventListener(FlexEvent.HIDE))
				element.removeEventListener(FlexEvent.HIDE, dataFormItem_hideHandler);
			
			if (IDataFormItem(element).validators != null && IDataFormItem(element).validators.length > 0)
				unregisterValidators(IDataFormItem(element).validators);
		}
		
		private function this_elementAddHandler(event:ElementExistenceEvent):void
		{
			if (event.target is MyFormItem)
			{
				var parent:Object = event.target.parent;
				
				while (parent != this)
				{
					if (parent is MyForm) return;
					parent = parent.parent;
				}
				distributeData(enumerateChildren(event.target));
			}
		}
		
		private function this_elementRemoveHandler(event:ElementExistenceEvent):void
		{
			if (event.target is MyFormItem && registeredItems.indexOf(event.target) != -1)
				denumerateChildren(event.target);
		}
		
		//----------------------------------------------------------------------
		//
		//  Overriden Methods
		//
		//----------------------------------------------------------------------
		
		/**
		 *  @private
		 */
		override protected function commitProperties():void
		{ 
			// If the data changed, ... .
			
			if (dataChanged)
			{	
				var localData:IPropertyChangeNotifier;
				if (_data != null)
				{
					if (_data is IPropertyChangeNotifier)
					{
						localData = IPropertyChangeNotifier(_data);
					}
					else
					{
						var uid:String;
						if (_data.hasOwnProperty("uid"))
							uid = _data["uid"];
						localData = new ObjectProxy(_data, uid);
					}
					proxyData = localData;
				}
				else
				{
					proxyData = null;
				}
				
				var tmp:Array = [];
				if (localData != null)
					tmp.push(localData);
				
				//		if (_dataCollection != null)
				//		{
				//			_dataCollection.source = tmp;
				//			dataCollectionChanged = false;
				//		}
				//		else
				//		{
				
				// Now we should distribute new data to IDataRenderer children.
				distributeData();
				
				dataChanged = false;
			}
			super.commitProperties();
			
		}
		
		/**
		 *  @private
		 */
		override protected function measure():void
		{
			super.measure();
		}
		
		/**
		 *  @private
		 */
		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
		{
			super.updateDisplayList(unscaledWidth, unscaledHeight);
		}
		
		
		//----------------------------------------------------------------------
		//
		//  Handlers
		//
		//----------------------------------------------------------------------
		
		/**
		 *  @private
		 *  Called on every <code>Event.ENTER_FRAME</code> event
		 *  when we are waiting to reset validation results.
		 *
		 *  @param event The enter frame event
		 *  
		 */
		private function this_enterFrameHandler(event:Event):void
		{
			framesToWait -= 1;
			if (framesToWait == 0)
			{
				if (hasEventListener(Event.ENTER_FRAME))
					removeEventListener(Event.ENTER_FRAME, this_enterFrameHandler);	
				resetValidation();
			}
		}
				
		/**
		 *  @private
		 *  Called when <code>trigger</code> dispatches <code>triggerEvent</code>.
		 *  By this event the data form calls <code>validateForm()</code> method.
		 *
		 *  @param event The event specified by the <code>triggerEvent</code> property
		 *  
		 */
		protected function trigger_triggerEventHandler(event:Event):void
		{
			// Trigger validation process.
			validateForm();
		}
		
		/**
		 *  @private
		 *  Called when validators within the <code>registeredValidators</code>
		 *  dispatch ValidationResultEvent.VALID or ValidationResultEvent.INVALID events.
		 *  We will catch certain events and modify the form validation state based on that.
		 *
		 *  @param event The validation result event
		 *  
		 */
		protected function validator_validationResultHandler(event:ValidationResultEvent):void
		{
			// TODO : implement registration logic for invalid validators ...	
			var validator:Validator = event.target as Validator;
			var index:int;
			if (event.type == ValidationResultEvent.VALID)
			{
				if (invalidValidators != null && invalidValidators.length > 0)
				{
					index = invalidValidators.indexOf(validator);
					if (index > -1)
						invalidValidators.splice(index, 1);
				}
			}
			else if (event.type == ValidationResultEvent.INVALID)
			{
				if (invalidValidators == null)
					invalidValidators = [];
				
				if (invalidValidators.length > 0)
				{
					index = invalidValidators.indexOf(validator);
					if (index < 0)
						invalidValidators.push(validator);
				}
				else
				{
					invalidValidators.push(validator);
				}
			}
			
			/*		// Check the form validation state.
			if (invalidValidators != null && invalidValidators.length > 0)
			{
			setIsValid(false);	
			}
			else
			{
			setIsValid(true);
			errorString = "";
			}*/
		}
		
		/**
		 *  @private
		 */ 
		protected function dataFormItem_enabledChangeHandler(event:Event):void
		{
			var dataFormItem:IDataFormItem = event.target as IDataFormItem;
			if (dataFormItem && dataFormItem.enabled)
			{
				if (dataFormItem.validators != null && dataFormItem.validators.length > 0)
					registerValidators(dataFormItem.validators);
			}
			else
			{
				if (dataFormItem.validators != null && dataFormItem.validators.length > 0)
					unregisterValidators(dataFormItem.validators);
			}
		}
		
		/**
		 *  @private
		 */ 
		protected function dataFormItem_showHandler(event:FlexEvent):void
		{
			var dataFormItem:IDataFormItem = event.target as IDataFormItem;
			if (dataFormItem &&
				dataFormItem.validators != null &&
				dataFormItem.validators.length > 0)
				registerValidators(dataFormItem.validators);
		}
		
		/**
		 *  @private
		 */ 
		protected function dataFormItem_hideHandler(event:FlexEvent):void
		{
			var dataFormItem:IDataFormItem = event.target as IDataFormItem;
			if (dataFormItem &&
				dataFormItem.validators != null &&
				dataFormItem.validators.length > 0)
				unregisterValidators(dataFormItem.validators);
		}
		
		override public function set errorString(value:String):void
		{
			super.errorString = value;
			
			new CSMessage(CSMessage.TYPE_ERROR, errorString).showAsStatus();
		}
	}
}
